#ifndef COMMON_CHANNEL_H
#define COMMON_CHANNEL_H

#include <brpc/channel.h>

#include <memory>
#include <mutex>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>

namespace chat {
/**
 * @brief 单个服务的信道管理类
 * @note 一个服务可以由多个主机提供，其就对应着多个信道
 */
class ServiceChannel {
   public:
    using ChannelPtr = std::shared_ptr<brpc::Channel>;

    /**
     * @brief 通过主机地址，添加可以提供该服务的信道
     * @param host[in]: 可以提供该服务的主机地址
     */
    bool append(const std::string& host);

    /**
     * @brief 通过主机地址，删除可以提供该服务的信道
     * @param host[in]: 可以提供该服务的主机地址
     */
    bool remove(const std::string& host);

    /**
     * @brief 通过 RR轮转，选择信道提供服务
     * @note  管理 channel 使用 vector 而不是set，
     *        原因是获取的频率要远大于添加/删除信道的频率
     */
    ChannelPtr choose();

   private:
    std::mutex mutex_;
    int index_ = 0;                     // 轮转下标
    std::string service_name_;          // 服务名称
    std::vector<ChannelPtr> channels_;  // 可以支持该服务的信道
    std::unordered_map<std::string, ChannelPtr>
        host_channel_map_;  // 主机地址和信道的映射关系
};

using ServiceChannelPtr = std::shared_ptr<ServiceChannel>;

/**
 * @brief 服务管理类
 * @note 可以同时存在多个服务，需要将其管理起来
 */
class ServiceManager {
   public:
    /**
     * @brief 获取一个具体服务的 channel
     * @param service_name[in]: 服务名称
     */
    ServiceChannel::ChannelPtr choose(const std::string& service_name);

    /**
     * @brief 申明要关心哪一个服务，不关心的服务就不需要管理
     * @param service_name[in]: 要关心的服务名称
     */
    void declared(const std::string& service_name);

    /**
     * @brief 服务上线时的回调函数
     * @param service_instance[in]: 服务实例名称
     * @param host[in]: 提供该服务的主机地址
     */
    void onServiceOnline(const std::string& service_instance,
                         const std::string& host);

    /**
     * @brief 服务下线时的回调函数
     * @param service_instance[in]: 服务实例名称
     * @param host[in]: 提供该服务的主机地址
     */
    void onServiceOffline(const std::string& service_instance,
                          const std::string& host);

    /**
     * @brief 获取服务实例的服务名称
     * @param service_instance[in]: 服务实例
     * @return service_name
     */
    std::string getServiceName(const std::string& service_instance);

   private:
    std::mutex mutex_;
    std::unordered_set<std::string> follow_services_;  // 关心的服务
    std::unordered_map<std::string, ServiceChannelPtr>
        services_;  // 服务名称和单个服务管理类的映射关系
};

using ServiceManagerPtr = std::shared_ptr<ServiceManager>;
}  // namespace chat

#endif  // COMMON_CHANNEL_H